/*
 * Copyright (c) 2006-2022, Ulandlink Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-10-25     xpw          first version
 */

#include <stdio.h>
#include <string.h>
#include "pin.h"
#include "drv_gpio.h"

#include <at_device_w15.h>
#include "app_sys_param.h"

#define LOG_TAG               "at.dev.w15"
#define LOG_LVL               LOG_LVL_DBG
#include <at_log.h>

#ifdef AT_DEVICE_USING_W15
#include "app_network_param.h"

#define W15_URC_MSG_SHOW_ENABLE 0
#define W15_WIFI_FAILED_TIMES 6

#define W15_HOTSPOT_ENABLE 1
#define W15_SERVER_ENABLE 1

static int wifi_failed_num = 0;
static char ping_resp_str[128]= {0};

/* =============================  w15 network interface operations ============================= */

static w15_device_info w15_info = {0};

static int w15_net_init(struct at_device *device);
static int w15_power_enable(struct at_device *device, rt_bool_t is_enabled);
static int w15_wifi_enable(struct at_device *device, rt_bool_t is_enabled);
static int w15_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled);

static int w15_netdev_set_up(struct netdev *netdev)
{
    struct at_device *device = RT_NULL;

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    if (netdev_is_up(netdev) == RT_FALSE) {
        w15_net_init(device);
        LOG_D("network interface device(%s) set up.", netdev->name);
    }

    return RT_EOK;
}


static int w15_netdev_set_down(struct netdev *netdev)
{
    struct at_device *device = RT_NULL;

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device by netdev(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    if (netdev_is_up(netdev)) {
        w15_power_enable(device, RT_FALSE);
        device->is_init = RT_FALSE;
        
        netdev_low_level_set_status(netdev, RT_FALSE);
        netdev_low_level_set_link_status(netdev, RT_FALSE);
        //netdev_low_level_set_dhcp_status(netdev, RT_FALSE);
        LOG_D("network interface device(%s) set down status", netdev->name);
    }

    return RT_EOK;
}

static int w15_netdev_set_addr_info(struct netdev *netdev, ip_addr_t *ip_addr, ip_addr_t *netmask, ip_addr_t *gw)
{
#define IPADDR_RESP_SIZE                128
#define IPADDR_SIZE                     16
#define W15_WAIT_WIFI_DISCON_TIMEOUT    (3 * RT_TICK_PER_SECOND)

    int result = RT_EOK;
    rt_tick_t start_time;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    char ip_str[IPADDR_SIZE] = {0};
    char gw_str[IPADDR_SIZE] = {0};
    char netmask_str[IPADDR_SIZE] = {0};
    rt_bool_t need_enable_wifi = RT_FALSE;

    RT_ASSERT(netdev);
    RT_ASSERT(ip_addr || netmask || gw);

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp(IPADDR_RESP_SIZE, 0, rt_tick_from_millisecond(300));
    if (resp == RT_NULL) {
        LOG_E("no memory for resp create.");
        result = -RT_ENOMEM;
        goto __exit;
    }

    /* Convert numeric IP address into decimal dotted ASCII representation. */
    if (ip_addr) {
        rt_memcpy(ip_str, inet_ntoa(*ip_addr), IPADDR_SIZE);
    } else {   
        rt_memcpy(ip_str, inet_ntoa(netdev->ip_addr), IPADDR_SIZE); 
    }

    if (gw) {
        rt_memcpy(gw_str, inet_ntoa(*gw), IPADDR_SIZE);
    } else {
        rt_memcpy(gw_str, inet_ntoa(netdev->gw), IPADDR_SIZE);
    }
    
    if (netmask) {
        rt_memcpy(netmask_str, inet_ntoa(*netmask), IPADDR_SIZE);
    } else {
        rt_memcpy(netmask_str, inet_ntoa(netdev->netmask), IPADDR_SIZE);   
    }
    
    if ((netdev_is_link_up(device->netdev) == RT_TRUE)){
        w15_wifi_enable(device, RT_FALSE);
        need_enable_wifi = RT_TRUE;
    }
    
    start_time = rt_tick_get();
    
    while (1) {        
        /* check if wifi disconnect timeout */
        if (rt_tick_get() - start_time > W15_WAIT_WIFI_DISCON_TIMEOUT) {
            LOG_E("device(%s) wait WiFi disconnect timeout(%d tick).", netdev->name, W15_WAIT_WIFI_DISCON_TIMEOUT);
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        
        if (netdev_is_link_up(device->netdev) == RT_FALSE) {
            if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,101,%s", ip_str) < 0) {
                LOG_E("device(%s) config WiFi ipv4 failed.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }
            
            if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,102,%s", netmask_str) < 0) {
                LOG_E("device(%s) config WiFi netmask failed.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }
            
            if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,103,%s", gw_str) < 0) {
                LOG_E("device(%s) config WiFi gateway failed.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }
            
            LOG_I("device(%s) config WiFi addr info success.", device->name);
            
            if (need_enable_wifi) {
                w15_wifi_enable(device, RT_TRUE);
            }
            
            break;
        }
    }
    
__exit:
    
    if (resp) {
        at_delete_resp(resp);
    }

    return result;
}

static int w15_netdev_set_addr_info_by_str(struct netdev *netdev, char *ip_addr_str, char *netmask_str, char *gw_str)
{
    int result;
    
    ip_addr_t *ip_addr, *netmask, *gw;
    
    if (ip_addr_str != RT_NULL) {
        ip_addr = rt_malloc(sizeof(ip_addr_t));
        inet_aton(ip_addr_str, ip_addr);
    }
    
    if (netmask_str != RT_NULL) {
        netmask = rt_malloc(sizeof(ip_addr_t));
        inet_aton(netmask_str, netmask);
    }
    
    if (gw_str != RT_NULL) {
        gw = rt_malloc(sizeof(ip_addr_t));
        inet_aton(gw_str, gw);
    }
    
    if (ip_addr || netmask || gw) {
        result = w15_netdev_set_addr_info(netdev, ip_addr, netmask, gw);
    } else {
        result = RT_ERROR;
    }
    
__exit:
    
    if (ip_addr != RT_NULL) {
        rt_free(ip_addr);
    }
    
    if (netmask != RT_NULL) {
        rt_free(netmask);
    }
    
    if (gw != RT_NULL) {
        rt_free(gw);
    }
    
    return result;
}

static int w15_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server)
{
#define IPADDR_RESP_SIZE                128
#define IPADDR_SIZE                     16
#define W15_WAIT_WIFI_DISCON_TIMEOUT    (3 * RT_TICK_PER_SECOND)

    int result = RT_EOK;
    rt_tick_t start_time;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    char dns_server_str[IPADDR_SIZE] = {0};

    RT_ASSERT(netdev && dns_server);

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }
    
    if ((netdev_is_link_up(device->netdev) == RT_TRUE)){
        w15_wifi_enable(device, RT_FALSE);
    }

    resp = at_create_resp(IPADDR_RESP_SIZE, 0, rt_tick_from_millisecond(300));
    if (resp == RT_NULL) {
        LOG_E("no memory for resp create.");
        result = -RT_ENOMEM;
        goto __exit;
    }

    rt_memcpy(dns_server_str, inet_ntoa(*dns_server), IPADDR_SIZE);
    
    start_time = rt_tick_get();
    
    while (1) {        
        /* check if wifi disconnect timeout */
        if (rt_tick_get() - start_time > W15_WAIT_WIFI_DISCON_TIMEOUT) {
            LOG_E("device(%s) wait WiFi disconnect timeout(%d tick).", netdev->name, W15_WAIT_WIFI_DISCON_TIMEOUT);
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        
        if (netdev_is_link_up(device->netdev) == RT_FALSE) {
            if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,%d,%s", 104 + dns_num, dns_server_str) < 0) {
                LOG_E("device(%s) config WiFi dns server(%d) failed.", dns_num, device->name);
                result = -RT_ERROR;
                goto __exit;
            }
            
            LOG_I("device(%s) config WiFi dns server(%d) success.", dns_num, device->name);
            
            break;
        }
    }
    
__exit:
    
    if (resp) {
        at_delete_resp(resp);
    }

    return result;
}

static int w15_netdev_set_dns_server_by_str(struct netdev *netdev, char *dns_server0_str, char *dns_server1_str)
{
    int result;
    struct at_device *device = RT_NULL;
    rt_bool_t need_enable_wifi = RT_FALSE;
    
    RT_ASSERT(netdev && dns_server0_str);

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }
    
    if ((netdev_is_link_up(device->netdev) == RT_TRUE)){
        w15_wifi_enable(device, RT_FALSE);
        need_enable_wifi = RT_TRUE;
    }
    
    ip_addr_t *dns_server = rt_malloc(sizeof(ip_addr_t));
    inet_aton(dns_server0_str, dns_server);
    result = w15_netdev_set_dns_server(netdev, 0, dns_server);
    
    rt_memset(dns_server, 0, sizeof(dns_server));
    
    if (dns_server1_str != RT_NULL && result == RT_EOK) {
        inet_aton(dns_server1_str, dns_server);
    }
    
    w15_netdev_set_dns_server(netdev, 1, dns_server);
    
    if (need_enable_wifi) {
        w15_wifi_enable(device, RT_TRUE);
    }
    
    rt_free(dns_server);
    
    return result;
}

static int w15_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled)
{
#define W15_WAIT_WIFI_DISCON_TIMEOUT    (3 * RT_TICK_PER_SECOND)

    int result = RT_EOK;
    rt_tick_t start_time = 0;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    rt_bool_t need_enable_wifi = RT_FALSE;

    RT_ASSERT(netdev);
    
    if (netdev_is_dhcp_enabled(netdev) == is_enabled) {
        LOG_W("device(%s) WiFi DHCP is already been %s.", netdev->name, is_enabled == RT_TRUE ? "enabled" :  "disabled");
        goto __exit;
    }

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    
    if (device == RT_NULL) {
        LOG_E("get device by netdev(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp(128, 0, RT_TICK_PER_SECOND);
    if (resp == RT_NULL) {
        LOG_E("no memory for resp struct.");
        return -RT_ENOMEM;
    }
        
    if ((netdev_is_link_up(device->netdev) == RT_TRUE)){
        w15_wifi_enable(device, RT_FALSE);
        need_enable_wifi = RT_TRUE;
    }
    
    start_time = rt_tick_get();
    
    while (1) {        
        /* check if wifi disconnect timeout */
        if (rt_tick_get() - start_time > W15_WAIT_WIFI_DISCON_TIMEOUT) {
            LOG_E("device(%s) wait WiFi disconnect timeout(%d tick).", netdev->name, W15_WAIT_WIFI_DISCON_TIMEOUT);
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        
        if (netdev_is_link_up(device->netdev) == RT_FALSE) {
            if (!is_enabled) {
                if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,100,1") < 0) {
                    LOG_E("device(%s) disable WiFi DHCP failed.", device->name);
                    result = -RT_ERROR;
                    goto __exit;
                }
                LOG_I("device(%s) disable WiFi DHCP success.", device->name);
                if (need_enable_wifi) {
                    w15_wifi_enable(device, RT_TRUE);
                }
                break;
            } else {
                if (at_obj_exec_cmd(device->client, resp, "AT+UWSC=1,100,2") < 0) {
                    LOG_E("device(%s) enable WiFi DHCP failed.", device->name);
                    result = -RT_ERROR;
                    goto __exit;
                }
                LOG_I("device(%s) enable WiFi DHCP success.", device->name);
                if (need_enable_wifi) {
                    w15_wifi_enable(device, RT_TRUE);
                }
                break;
            }
        }
    }
    
__exit:
    
    if (resp) {
        at_delete_resp(resp);
    }

    return result;
}

#ifdef NETDEV_USING_PING
static int w15_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)
{
#define W15_PING_IP_SIZE            16
#define W15_GET_PING_RESP_TIMEOUT   6 * RT_TICK_PER_SECOND

    rt_err_t result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    char ip_addr[W15_PING_IP_SIZE] = {0};
    int req_time, ttl, error_code, start_time;

    RT_ASSERT(netdev);
    RT_ASSERT(host);
    RT_ASSERT(ping_resp);

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp(64, 0, timeout);
    if (resp == RT_NULL) {
        LOG_E("no memory for resp create.");
        return -RT_ENOMEM;
    }

    w15_ping_resp_clear();
    
    if (at_obj_exec_cmd(device->client, resp, "AT+UPING=\"%s\",1", host) < 0) {
        result = -RT_ERROR;
        goto __exit;
    }
    
    start_time = rt_tick_get();
    
    while (1) {
        if (rt_tick_get() - start_time > W15_GET_PING_RESP_TIMEOUT) {
            return -RT_ETIMEOUT;
        }
        
        if (*ping_resp_str != '\0') {
            break;
        }
        
        rt_thread_delay(300);
    }
    
    if (rt_strstr(ping_resp_str, "+UUPING:") != RT_NULL) {
        sscanf(ping_resp_str, "+UUPING:%*d,%d,\"%*[^,],\"%[^\"]\",%d,%d", &data_len, ip_addr, &ttl, &req_time);
        if (req_time == -1 ) {
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        inet_aton(ip_addr, &(ping_resp->ip_addr));
        ping_resp->data_len = data_len;
        ping_resp->ttl = ttl;
        ping_resp->ticks = req_time;
    } else if (rt_strstr(ping_resp_str, "+UUPINGER:") != RT_NULL) {
        sscanf(ping_resp_str, "+UUPINGER:%d", &error_code);
        if (error_code == 3) {
            result=  -RT_ETIMEOUT;
        } else {
            result = -RT_ERROR;
        }
    }
    
__exit:
    if (resp) {
        at_delete_resp(resp);
    }

    return result;
}
#endif /* NETDEV_USING_PING */

#ifdef NETDEV_USING_NETSTAT
void w15_netdev_netstat(struct netdev *netdev)
{
    int count, peer_id;
    char key[10] = {0};
    char type[5] = {0};
    char list[512] = {0};
    int parsed_count = 0;
    char local_host[32] = {0};
    char remote_host[32] = {0};
    char connection[128] = {0};
    at_response_t resp = RT_NULL;
    int status, result = RT_ERROR;
    struct at_device *device = RT_NULL;

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", netdev->name);
        return;
    }
    
    resp = at_create_resp(256, 0, RT_TICK_PER_SECOND);
    if (resp == RT_NULL ) {
        LOG_E("no memory for resp create.");
        return;
    }
    
    if (at_obj_exec_cmd(device->client, resp, "AT+UDLP?") < 0) {
        LOG_E("get device(%s) connected peers and lists failed.", device->name);
        goto __exit;
    }
    
    count = resp->line_counts - 3;
    
    for (int i = 0;; i ++) {
        
        rt_memset(key, 0, sizeof(key));
        rt_memset(type, 0, sizeof(type));
        rt_memset(local_host, 0, sizeof(local_host));
        rt_memset(connection, 0, sizeof(connection));
        rt_memset(remote_host, 0, sizeof(remote_host));
        
        rt_snprintf(key, sizeof(key), "+UDLP:%d", i);
        if (at_resp_parse_line_args_by_kw(resp, key, "+UDLP:%d,\"%[^\"]\",\"%[^\"]\",\"%[^\"]\"",&peer_id, type, local_host, remote_host) <= 0){
            //LOG_E("parse device(%s) connected peers and lists failed.", device->name);
            //goto __exit;
            continue;
        }
        
        rt_snprintf(connection, sizeof(connection), "connection %d: %s: %s => %s\n", peer_id, type, local_host, remote_host);
        strcat(list, connection);
        
        parsed_count ++;
        
        if (parsed_count >= count) {
            break;
        }
        
    }
    
    rt_kprintf("\n%s\n", list);
    
__exit:
    
    if (resp) {
        at_delete_resp(resp);
    }
    
    return;
}
#endif /* NETDEV_USING_NETSTAT */

static const struct netdev_ops w15_netdev_ops =
{
    .set_up             = w15_netdev_set_up,
    .set_down           = w15_netdev_set_down,

    .set_addr_info      = w15_netdev_set_addr_info,
    .set_dns_server     = w15_netdev_set_dns_server,
    .set_dhcp           = w15_netdev_set_dhcp,

#ifdef NETDEV_USING_PING
    .ping               = w15_netdev_ping,
#endif
#ifdef NETDEV_USING_NETSTAT
    .netstat            = w15_netdev_netstat,
#endif
    
    .set_default        = RT_NULL,
};

static struct netdev *w15_netdev_add(const char *netdev_name)
{
#define ETHERNET_MTU        1500
#define HWADDR_LEN          6
    struct netdev *netdev = RT_NULL;

    RT_ASSERT(netdev_name);

    netdev = netdev_get_by_name(netdev_name);
    if (netdev != RT_NULL) {
        return (netdev);
    }

    netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
    if (netdev == RT_NULL) {
        LOG_E("no memory for netdev create.");
        return RT_NULL;
    }

    netdev->mtu         = ETHERNET_MTU;
    netdev->ops         = &w15_netdev_ops;
    netdev->hwaddr_len  = HWADDR_LEN;

#ifdef SAL_USING_AT
    extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
    /* set the network interface socket/netdb operations */
    sal_at_netdev_set_pf_info(netdev);
#endif

    netdev_register(netdev, netdev_name, RT_NULL);

    return netdev;
}

/* =============================  w15 device operations ============================= */
                                                                                \
static int w15_hotspot_enable(struct at_device *device, rt_bool_t is_enabled)
{
#define W15_HOTSPOT_RETRY_TIMES     3
    
    char sn[128] = {0};
    int retry_times = 0;
    at_response_t resp = RT_NULL;
    int status, result = RT_ERROR;
    struct at_device_w15 *w15   = (struct at_device_w15 *) device->user_data;
    
    resp = at_create_resp(256, 0, RT_TICK_PER_SECOND);
    if (resp == RT_NULL ) {
        LOG_E("no memory for resp create.");
        goto __exit;
    }
    
    while (1) {
    
        if (is_enabled) {

            app_sys_param_get(APP_SYS_PARAM_SN, sn, sizeof(sn));
            
            /* get hotspot status */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPSTAT=3") < 0) {
                LOG_E("device(%s) get hotspot status error.", device->name);
                goto __exit;
            }
            
            /* parse hotspot status */
            if (at_resp_parse_line_args_by_kw(resp, "+UWAPSTAT:3", "+UWAPSTAT:3,%d", &status) <= 0)
            {
                LOG_E("device(%s) parse hotspot status error.", device->name);
                goto __exit;
            }
            
            if (status == 1) {
                LOG_W("device(%s) hotspot is already enabled.", device->name);
                result = RT_EOK;
                goto __exit;
            }
            
            /* set hotspot ssid */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,2,\"Charger-No.%s\"", sn) < 0) {
                LOG_E("device(%s) set hotspot ssid error.", device->name);
                goto __exit;
            }
            
            /* set hotspot password */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,8,\"123456789\"") < 0) {
                LOG_E("device(%s) set hotspot password error.", device->name);
                goto __exit;
            }
            
            /* set hotspot DHCP enable */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,106,1", sn) < 0) {
                LOG_E("device(%s) set hotspot DHCP error.", device->name);
                goto __exit;
            }
            /* set hotspot ip address */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,101,192.168.4.1") < 0) {
                LOG_E("device(%s) set hotspot ip error.", device->name);
                goto __exit;
            }
            /* set hotspot default gateway */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,103,192.168.4.1") < 0) {
                LOG_E("device(%s) set hotspot gateway error.", device->name);
                goto __exit;
            }
            
            /* set hotspot security mode (WPA/WPA2 mixed mode) */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPC=0,5,3,2") < 0) {
                LOG_E("device(%s) set hotspot security mode error.", device->name);
                goto __exit;
            }
            
            /* activate hotspot config */
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPCA=0,3") < 0) {
                LOG_E("device(%s) hotspot init error.", device->name);
                goto __exit;
            }
            
            result = RT_EOK;
            
        } else {
            
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPSTAT=3") < 0) {
                LOG_E("device(%s) get hotspot status error.", device->name);
                goto __exit;
            }
            
            if (at_resp_parse_line_args_by_kw(resp, "+UWAPSTAT:3", "+UWAPSTAT:3,%d", &status) <= 0) {
                LOG_E("device(%s) parse hotspot status error.", device->name);
                goto __exit;
            }
            
            if (status == 0) {
                LOG_W("device(%s) hotspot is already disabled.", device->name);
                result = RT_EOK;
                goto __exit;
            }
            
            if (at_obj_exec_cmd(device->client, resp, "AT+UWAPCA=0,4") < 0) {
                LOG_E("device(%s) hotspot deactivate error.", device->name);
            }
            
            result = RT_EOK;
        }
        
__exit:
        
        if (result != RT_EOK && retry_times < W15_HOTSPOT_RETRY_TIMES) {
            LOG_W("%s device(%s) hotspots retry...", is_enabled ? "enable" : "disable", device->name);
            retry_times ++;
            continue;
        }
        
        break;
    }
    
    if (resp) {
        at_delete_resp(resp);
    }
    
    return result;
}

static int w15_wifi_enable(struct at_device *device, rt_bool_t is_enabled)
{  
#define W15_WIFI_RETRY_TIMES     3  
    
    int retry_times = 0;
    int status, result = RT_ERROR;
    at_response_t resp = RT_NULL;
    struct at_device_w15 *w15   = (struct at_device_w15 *) device->user_data;
    
    resp = at_create_resp(256, 0, RT_TICK_PER_SECOND);
    if (resp == RT_NULL ) {
        LOG_E("no memory for resp create.");
        goto __exit;
    }
    
    while (1) {
        
        if (is_enabled) {
            
            if (w15->ssid.ssid_len == 0) {
                LOG_E("system parameter WiFi ssid or password is null");
                result = RT_EOK;
                goto __exit;
            }
        
            device->is_init = RT_TRUE;
            
            if ((netdev_is_link_up(device->netdev) == RT_TRUE)){
                LOG_W("device(%s) WiFi is already connected.", device->name);
                result = RT_EOK;
                goto __exit; 
            }
            
            if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 512, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSCA=1,4") < 0) {
                LOG_E("device(%s) deactivate WiFi config failed.", device->name);
                goto __exit;
            }

            /* connect to input wifi ap */
            if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSC=1,2,\"%s\"", w15->ssid.ssid_val) < 0) {
                LOG_E("device(%s) set WiFi ssid error.", device->name);
                goto __exit;
            }
            
            if ((w15->as & 0x01) == 0x01) {
                if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSC=1,5,1") < 0) {
                    LOG_E("device(%s) set WiFi authentication error.", device->name);
                    goto __exit;
                }
            } else if ((w15->as & 0x02) == 0x02 ) {
                if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSC=1,5,2") < 0) {
                    LOG_E("device(%s) set WiFi authentication error.", device->name);
                    goto __exit;
                }
                
                if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSC=1,8,\"%s\"", w15->password.pwd_val) < 0) {
                    LOG_E("device(%s) set WiFi password error.", device->name);
                    goto __exit;
                }                
            }
            
            if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSCA=1,3") < 0) {
                LOG_E("device(%s) activate WiFi config failed.", device->name);
                goto __exit;
            }
            
            result = RT_EOK;
            wifi_failed_num = 0;
            
        } else {
            device->is_init = RT_FALSE;
        
            if ((netdev_is_link_up(device->netdev) == RT_FALSE)) {
                LOG_W("device(%s) WiFi is already disabled.", device->name);
                result = RT_EOK;
                goto __exit;
            }
                
            if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSCA=1,4") < 0) {
                LOG_E("device(%s) deactivate WiFi config failed.", device->name);
                goto __exit;
            }
            
            result = RT_EOK;
        }
                
__exit:
        
        if (result != RT_EOK && retry_times < W15_HOTSPOT_RETRY_TIMES) {
            LOG_W("%s device(%s) wifi retry...", is_enabled ? "enable" : "disable", device->name);
            retry_times ++;
            continue;
        }
        
        break;
    }
    
    if (resp) {
        at_delete_resp(resp);
    }
    
    return result;
}

static int w15_hw_reset(struct at_device *device);
static void w15_netdev_info_delay_work(struct at_device *device);

static void w15_init_thread_entry(void *parameter)
{
    #define W15_WAIT_CONNECT_TIME    (3 * RT_TICK_PER_SECOND)
    #define INIT_RETRY               5  
    #define WAIT_RETRY               3  
    
    struct at_device *device    = (struct at_device *) parameter;
    struct at_device_w15 *w15   = (struct at_device_w15 *) device->user_data;
    struct at_client *client    = device->client;
    at_response_t resp          = RT_NULL;
    rt_err_t result             = RT_EOK;
    
    LOG_D("device(%s) initialize start.", device->name);

    resp = at_create_resp(256, 0, 5 * RT_TICK_PER_SECOND);
    if (resp == RT_NULL) {
        LOG_E("no memory for resp create.");
        return;
    }

    while (1) {
        /* power on the w15 device */
        w15_power_enable(device, RT_TRUE);
        
        /* wait w15 device startup finish */
        for (uint8_t i = 0; i < WAIT_RETRY; i++){
            result = at_client_obj_wait_connect(client, W15_WAIT_CONNECT_TIME);
            if (result == RT_EOK) {
                break;
            }
        }
        if (result != RT_EOK){
            goto __exit;
        }
		
		result = at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+GMR");
		if (result == RT_EOK) {
			char version[30] = {0};
			at_resp_parse_line_args_by_kw(resp, "\"", "\"%[^\"]\"", &w15_info.version);
			LOG_I("software version: %s", w15_info.version);	  
        } 
		
 		result = at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWAPMACADDR");
		if (result == RT_EOK){
			char scan_mac[128] = {0};
			if (at_resp_parse_line_args_by_kw(resp, "+UWAPMACADDR:", "+UWAPMACADDR:%s", &scan_mac[0]) > 0 ) {
				LOG_I("STA mac: %s", &scan_mac[0]);
				memcpy(w15_info.wifi_mac, scan_mac, sizeof(w15_info.wifi_mac));
			} else {
				result = -RT_ETIMEOUT;
				 goto __exit;
			}
		} else {				
			result = -RT_ETIMEOUT;
			goto __exit;		
		}
		
        if ( w15->ssid.ssid_len != 0) {
            result = at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 20 * RT_TICK_PER_SECOND), "AT+UWSCAN=\"%s\"", w15->ssid.ssid_val);
            if (result == RT_EOK) {
                char ssid[32] = {0};
                char bssid[32] = {0};
                int rssi, op_mode, channel, as, uc, gc;
                
                if (at_resp_parse_line_args_by_kw(resp, "+UWSCAN:", "+UWSCAN:%[^,],%d,\"%[^\"]\",%d,%d,%d,%d,%d", &bssid, &op_mode, &ssid, &channel, &rssi, &as, &uc, &gc) > 0 ) {
                    LOG_I("WIFI AP bssid(%s) op_mode(%d) ssid(%s) channel(%d) rssi(%d) as(%d) uc(%d) gc(%d)", bssid, op_mode, ssid, channel, rssi, as, uc, gc);
                    w15->as = as;
                    w15_wifi_enable(device, RT_TRUE);
                }                
            }
        }
        
#if W15_HOTSPOT_ENABLE
        w15_hotspot_enable(device, RT_TRUE);         
#endif

#if W15_SERVER_ENABLE
        w15_server_enable(device, RT_TRUE);
        extern void http_server_start(void);
        http_server_start();
#endif
        if (result != RT_EOK){
            goto __exit;
        }
        /* initialize successfully  */
        result = RT_EOK;
        break;

__exit:
        if (result != RT_EOK) {
            /* power off the w15 device */
            w15_power_enable(device, RT_FALSE);
            LOG_W("device(%s) initialize retry...", device->name);
        }
    }

    if (resp) {
        at_delete_resp(resp);
    }
        
    if (result == RT_EOK) {
        netdev_low_level_set_status(device->netdev, RT_TRUE);
        LOG_I("device(%s) network initialize.", device->name);
    } else {
        device->is_init = RT_FALSE;
        netdev_low_level_set_status(device->netdev, RT_FALSE);
        LOG_E("device(%s) network initialize failed.", device->name);
    }
}

static int w15_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_W15_INIT_ASYN
    
    #define W15_THREAD_STACK_SIZE      2048
    #define W15_THREAD_PRIORITY        (RT_THREAD_PRIORITY_MAX / 2)
    
    rt_thread_t tid;

    tid = rt_thread_create("w15_net", 
                            w15_init_thread_entry, 
                            (void *) device,
                            W15_THREAD_STACK_SIZE, 
                            W15_THREAD_PRIORITY, 20);
    if (tid) {
        rt_thread_startup(tid);
        LOG_I("create device(%s) init thread success.", device->name);
    } else {
        LOG_E("create device(%s) init thread failed.", device->name);
        return -RT_ERROR;
    }
#else
    w15_init_thread_entry(device);
#endif /* AT_DEVICE_W15_INIT_ASYN */

    return RT_EOK;
}

/* =============================  w15 device work funtion start ============================= */
static void w15_get_netdev_info(struct rt_work *work, void *work_data)
{
#define AT_ADDR_LEN          32
#define AT_ERR_DNS_SERVER    "255.255.255.255"
    
    at_response_t resp = RT_NULL;
    int fail_times = 0;
    int result = RT_ERROR;
    const char *resp_expr = "%*[^\"]\"%[^\"]\"";
    ip_addr_t ip_addr;
    rt_uint32_t mac_addr[6] = {0};
    rt_uint32_t num;
    rt_uint8_t dhcp_stat = 0;
    struct at_device *device = (struct at_device *)work_data;
    struct netdev *netdev = device->netdev;
    struct at_client *client = device->client;
    
    resp = at_create_resp(1024, 0, 20 * RT_TICK_PER_SECOND);
    if (resp == RT_NULL) {
        LOG_E("no memory for resp create.");
        result = -RT_ENOMEM;
        goto __exit;
    }
    
    while (1) {
        char ip[AT_ADDR_LEN] = {0};
        char mac[AT_ADDR_LEN] = {0};
        char gateway[AT_ADDR_LEN] = {0};
        char netmask[AT_ADDR_LEN] = {0};
        char dns_server1[AT_ADDR_LEN] = {0};
        char dns_server2[AT_ADDR_LEN] = {0};        

        if (at_obj_exec_cmd(client, resp, "AT+UWSC=1,100") < 0) {
            result = -RT_ETIMEOUT;
            goto __exit;
        }

        if (at_resp_parse_line_args_by_kw(resp, "+UWSC:1,100",  "+UWSC:1,100,%d", &dhcp_stat) <= 0) {
            LOG_E("device(%s) parse \"AT+UWSC=1,100\" cmd error.", device->name);
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        
        netdev_low_level_set_dhcp_status(netdev, dhcp_stat == 2 ? RT_TRUE : RT_FALSE);
    
        if (at_obj_exec_cmd(client, resp, "AT+UNSTAT=0") < 0) {
            result = -RT_ETIMEOUT;
            goto __exit;
        }

        if (at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,0",   "+UNSTAT:0,0,%s",    mac) <= 0 ||
            at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,101", "+UNSTAT:0,101,%s",  ip) <= 0 ||
            at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,102", "+UNSTAT:0,102,%s",  netmask) <= 0 ||
            at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,103",  "+UNSTAT:0,103,%s", gateway) <= 0 ||
            at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,104",  "+UNSTAT:0,104,%s", dns_server1) <= 0 ||
            at_resp_parse_line_args_by_kw(resp, "+UNSTAT:0,105",  "+UNSTAT:0,105,%s", dns_server2) <= 0 ) {
            LOG_E("device(%s) parse \"AT+UNSTAT=0\" cmd error.", device->name);
            result = -RT_ETIMEOUT;
            goto __exit;
        }
            
        if (!rt_strcmp(ip, "0.0.0.0") && netdev_is_dhcp_enabled(netdev) && netdev_is_link_up(device->netdev) != RT_FALSE) {
            fail_times ++;
            if (fail_times >= 60) {
                result = -RT_ERROR;
                LOG_E("netdev(%s) can not get correct wifi info.", device->name);
                break;
            }
            rt_thread_mdelay(500);
            continue;
        }
        
        inet_aton(gateway, &ip_addr);
        netdev_low_level_set_gw(netdev, &ip_addr);
        
        inet_aton(netmask, &ip_addr);
        netdev_low_level_set_netmask(netdev, &ip_addr);
        
        inet_aton(ip, &ip_addr);
        netdev_low_level_set_ipaddr(netdev, &ip_addr);
        
        num = 6;
        while (num--) {
            sscanf(mac + num * 2, "%x", &mac_addr[num]);
            netdev->hwaddr[num] = mac_addr[num];
            *(mac + num * 2) = '\0';
        }
        
        if (rt_strlen(dns_server1) > 0 ) {
            inet_aton(dns_server1, &ip_addr);
            netdev_low_level_set_dns_server(netdev, 0, &ip_addr);
        }

        if (rt_strlen(dns_server2) > 0 ) {
            inet_aton(dns_server2, &ip_addr);
            netdev_low_level_set_dns_server(netdev, 1, &ip_addr);
        }
        
        if (netdev_is_link_up(device->netdev) != RT_FALSE){
            LOG_I("device(%s) get ipv4 address %s", device->name, ip);
        }
        
        result = RT_EOK;

__exit:
        
        if (result == -RT_ETIMEOUT) {
            LOG_W("get netdev(%s) info retry...", device->name);            
            continue;
        }
        
        break;
        
    }
        
    if (resp) {
        at_delete_resp(resp);
    }
    
    if (work) {
        rt_free(work);
    }

    return;
}

static void w15_netdev_info_delay_work(struct at_device *device)
{
    struct rt_work *net_work = RT_NULL;
    net_work = (struct rt_work *)rt_calloc(1, sizeof(struct rt_work));
    if (net_work == RT_NULL) {
        return;
    }

    rt_work_init(net_work, w15_get_netdev_info, (void *)device);
    rt_work_submit(net_work, RT_TICK_PER_SECOND);
}

/* =============================  w15 device urc start ============================= */
static void urc_ping_resp_func(struct at_client *client, const char *data, rt_size_t size)
{
    RT_ASSERT(data);

#if W15_URC_MSG_SHOW_ENABLE
    LOG_D("get urc: %.*s", size, data);
#endif
    w15_ping_resp_clear();
    rt_memcpy(ping_resp_str, data, size);
}

static void urc_startup_func(struct at_client *client, const char *data, rt_size_t size)
{
    RT_ASSERT(data);

#if W15_URC_MSG_SHOW_ENABLE
    LOG_D("get urc: %.*s", size, data);
#endif
}

static void urc_reset_func(struct at_client *client, const char *data, rt_size_t size)
{
    RT_ASSERT(data);
    
	  char reason[32] = {0};
    struct at_device *device = RT_NULL;
    char *client_name = client->device->parent.name;
	
#if W15_URC_MSG_SHOW_ENABLE
    LOG_D("get urc: %.*s", size, data);
#endif
		
//rst:0x1 (POWERON_RESET),boot:0x33 (SPI_FAST_FLASH_BOOT)
		sscanf(data, "rst:%*[^(](%[^)])", reason);
		device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
		
//		if (rt_strstr(reason, "POWERON_RESET")) {
//			LOG_I("device(%s) power on reset(%s).", device->name, reason);
//		} else {
//			rt_kprintf("device(%s) has been restarted(%s), system blocked...\n", device->name, reason);
//			while(1);
//		}
}

static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size)
{
    RT_ASSERT(client && data && size);
    
    int sta_id ,error_code;
    char sta_mac[32] = {0};
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    struct at_device_w15 *w15 = RT_NULL;
    char *client_name = client->device->parent.name;

#if W15_URC_MSG_SHOW_ENABLE
    LOG_D("get urc: %.*s", size, data);
#endif
    
    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
    if (device == RT_NULL) {
        LOG_E("get device(%s) failed.", client_name);
        return;
    }
    
    if (rt_strstr(data, "+UUWLE")){
        /* +UUWLE:<connection_id>,<bssid>,<channel> */
        if (netdev_is_up(device->netdev)){
            wifi_failed_num = 0;
            LOG_I("device(%s) WiFi is connected.", device->name);
            netdev_low_level_set_link_status(device->netdev, RT_TRUE); 
            netdev_set_default(device->netdev);
            w15_netdev_info_delay_work(device);
        }

    } else if (rt_strstr(data, "+UUWLD")) {
        /* +UUWLD:<connection_id>,<reason> */
        if (netdev_is_up(device->netdev)) {
            w15 = (struct at_device_w15 *) device->user_data;
            sscanf(data, "+UUWLD:%*d,%d", &error_code);
            switch (error_code) {
                case 1:
                    LOG_E("device(%s) WiFi ssid(%s) has been remote closed.", device->name, w15->ssid.ssid_val);
                    wifi_failed_num = W15_WIFI_FAILED_TIMES - 1;
                    break;
                case 2:
                    LOG_E("device(%s) WiFi ssid(%s) is out of range.", device->name, w15->ssid.ssid_val);
                    break;
                case 3:
                    LOG_E("device(%s) WiFi ssid(%s) is roaming.", device->name, w15->ssid.ssid_val);
                    break;
                case 4:
                    LOG_E("device(%s) WiFi connect failed, check ssid(%s) and password(%s).", device->name, w15->ssid.ssid_val, w15->password.pwd_val);
                    //wifi_failed_num = W15_WIFI_FAILED_TIMES - 1;
                    break;
                case 5:
                    LOG_I("device(%s) WiFi config is deactivated.", device->name);
                    if (wifi_failed_num < W15_WIFI_FAILED_TIMES) {
                        wifi_failed_num = W15_WIFI_FAILED_TIMES;
                    }
                    break;
                case 0:
                default:
                    LOG_E("device(%s) WiFi ssid(%s) cannot connect.", device->name, w15->ssid.ssid_val);
                    break;  
            }
            if (wifi_failed_num < W15_WIFI_FAILED_TIMES) {
                wifi_failed_num ++;
                /* wifi connect timeout and deactivate it */
                if (wifi_failed_num == W15_WIFI_FAILED_TIMES) {
                    resp = at_create_resp(64, 0, RT_TICK_PER_SECOND);
                    at_obj_exec_cmd(client, resp, "AT+UWSCA=1,4");
                }
            }              
            netdev_low_level_set_link_status(device->netdev, RT_FALSE);
            w15_netdev_info_delay_work(device);
        }     
    } else if (rt_strstr(data, "+UUWAPU")) {
        /* +UUWAPU:<id> */
        if (netdev_is_up(device->netdev)) {
            LOG_I("device(%s) WiFi hotspot enabled.", device->name);          
        }
    } else if (rt_strstr(data, "+UUWAPD")) {
        /* +UUWAPD:<id> */
        if (netdev_is_up(device->netdev)) { 
            LOG_I("device(%s) WiFi hotspot disabled.", device->name);          
        }
    } else if (rt_strstr(data, "+UUWAPSTAC")) {
        /* +UUWAPSTAC:<id>,<mac_addr> */
        sscanf(data, "+UUWAPSTAC:%d,%s", &sta_id, sta_mac);
        LOG_I("a divice(id:%d mac:%s) connected to device(%s) hotspot.", sta_id, sta_mac, device->name);
    } else if (rt_strstr(data, "+UUWAPSTAD")) {
        /* +UUWAPSTAD:<id> */
        sscanf(data, "+UUWAPSTAD:%d", &sta_id);
        LOG_I("a device(id:%d) disconnected to device(%s) hotspot.", sta_id, device->name);
    }

__exit:
    if (resp) {
        at_delete_resp(resp);
    }
}

static const struct at_urc w15_urc_table[] =
{   
    {"+STARTUP",                    "\r\n",           urc_startup_func},
    
    {"+UUPING",                     "\r\n",           urc_ping_resp_func},
    {"+UUPINGER",                   "\r\n",           urc_ping_resp_func},
    
    {"+UUWLE",                      "\r\n",           urc_connect_func},
    {"+UUWLD",                      "\r\n",           urc_connect_func},
    {"+UUWAPU",                     "\r\n",           urc_connect_func},
    {"+UUWAPD",                     "\r\n",           urc_connect_func},
    {"+UUWAPSTAC",                  "\r\n",           urc_connect_func},
    {"+UUWAPSTAD",                  "\r\n",           urc_connect_func},   
		{"rst:",												"\r\n",						urc_reset_func},
};

/* =============================  w15 device urc end ============================= */

/* =============================  w15 device init start ============================= */
static int w15_init(struct at_device *device)
{
    struct at_device_w15 *w15 = (struct at_device_w15 *) device->user_data;

    /* initialize AT client */
    at_client_init(w15->client_name, w15->recv_line_num);

    device->client = at_client_get(w15->client_name);
    if (device->client == RT_NULL) {
        LOG_E("get AT client(%s) failed.", w15->client_name);
        return -RT_ERROR;
    }

    /* register URC data execution function  */
    at_obj_set_urc_table(device->client, w15_urc_table, sizeof(w15_urc_table) / sizeof(w15_urc_table[0]));

#ifdef AT_USING_SOCKET
    w15_socket_init(device);
#endif

    /* add w15 device to the netdev list */
    device->netdev = w15_netdev_add(w15->device_name);
    if (device->netdev == RT_NULL) {
        LOG_E("add netdev(%s) failed.", w15->device_name);
        return -RT_ERROR;
    }

    /* initialize w15 pin configuration */
    if (w15->power_pin != -1) {
        rt_pin_mode(w15->power_pin, PIN_MODE_OUTPUT);
    }
    
    if (w15->reset_pin != -1) {
        rt_pin_mode(w15->reset_pin, PIN_MODE_OUTPUT);
    }
	
	//	.rts_pin        = GET_PIN(C, 2),      
//	.cts_pin        = GET_PIN(F, 10), 
	rt_pin_mode(GET_PIN(C, 2), PIN_MODE_OUTPUT);
	rt_pin_mode(GET_PIN(F, 10), PIN_MODE_OUTPUT);
	rt_pin_write(GET_PIN(F, 10), PIN_LOW);
	rt_pin_write(GET_PIN(C, 2), PIN_LOW);
    
    
//	rt_pin_mode(GET_PIN(E, 0), PIN_MODE_INPUT);
//	rt_pin_mode(GET_PIN(E, 1), PIN_MODE_INPUT);
//	rt_pin_write(GET_PIN(E, 0), PIN_HIGH);
//	rt_pin_write(GET_PIN(E, 1), PIN_HIGH);
//    
//	rt_pin_mode(GET_PIN(C, 1), PIN_MODE_OUTPUT);
//	rt_pin_write(GET_PIN(C, 1), PIN_LOW);
//    
//    while(1);
    
    /* initialize w15 device network */
    return w15_netdev_set_up(device->netdev);
}
/* =============================  w15 device init end ============================= */

/* =============================  w15 device deinit start ============================= */
static int w15_deinit(struct at_device *device)
{
    return w15_netdev_set_down(device->netdev);
}
/* =============================  w15 device deinit end ============================= */

/* =============================  w15 device control start============================= */
static int w15_power_enable(struct at_device *device, rt_bool_t is_enabled)
{
    int result = RT_EOK;
    struct at_device_w15 *w15 = RT_NULL;

    w15 = (struct at_device_w15 *)device->user_data;

    /* not nead to set pin configuration for w15 device power on */
    if (w15->power_pin == -1) {
        return -RT_ERROR;
    }
    
    if (is_enabled){
        rt_pin_write(w15->power_pin, PIN_LOW);
        LOG_D("device(%s) power on");
    } else {
        rt_pin_write(w15->power_pin, PIN_HIGH);
        LOG_D("device(%s) power off");
    }

    rt_thread_mdelay(5 * RT_TICK_PER_SECOND);
    
    return result;
}

static int w15_hw_reset(struct at_device *device)
{
    int result = RT_EOK;
    struct at_device_w15 *w15 = RT_NULL;

    w15 = (struct at_device_w15 *)device->user_data;

    /* not nead to set pin configuration for w15 device reset */
    if (w15->reset_pin == -1) {
        return -RT_ERROR;
    }
    
    LOG_W("device(%s) hardware reset...", w15->device_name);
    
    rt_pin_write(w15->reset_pin, PIN_HIGH);
    rt_thread_mdelay(100);
    rt_pin_write(w15->reset_pin, PIN_LOW);    
    rt_thread_mdelay(2000);
    
    return result;
}

/* reset w15 device and initialize device network again */
static int w15_reset(struct at_device *device)
{
    int result = RT_EOK;
    struct at_client *client = device->client;
    
    /* w15 device is_init to FALSE */
    device->is_init = RT_FALSE;

    /* initialize w15 device network */
    w15_net_init(device);

    return result;
}

/* change w15 wifi ssid and password information */
static int w15_wifi_info_set(struct at_device *device, struct at_device_ssid_pwd *info)
{
    return RT_EOK;
}

static int w15_wifi_get_signal(struct at_device *device, int16_t *rssi)
{
#define W15_SIGNAL_RESP_SIZE      256
#define W15_SIGNAL_RESP_TIMO      RT_TICK_PER_SECOND
    
    int t_rssi;
    int result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device_w15 *w15   = (struct at_device_w15 *) device->user_data;
    
    resp = at_create_resp(W15_SIGNAL_RESP_SIZE, 0, W15_SIGNAL_RESP_TIMO);
    if (resp == RT_NULL ) {
        LOG_E("no memory for resp create.");
        result = -RT_ERROR;
        goto __exit;
    }

    if (netdev_is_link_up(device->netdev) == RT_FALSE){
        goto __exit;
    } else {
        if (at_obj_exec_cmd(device->client, resp, "AT+UWSSTAT") < 0) {
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        
        if (at_resp_parse_line_args_by_kw(resp, "+UWSSTAT:6,", "+UWSSTAT:6,%d", &t_rssi) > 0 ) {
            *rssi = t_rssi;
        }
    }

 __exit:
    if (resp) {
        at_delete_resp(resp);
    }

    return result;
}

/* =============================  w15 device control end============================= */

static int w15_control(struct at_device *device, int cmd, void *arg)
{
    int result = RT_EOK;

    RT_ASSERT(device);

    switch (cmd)
    {
    case AT_DEVICE_CTRL_POWER_ON:
        result = w15_power_enable(device, RT_TRUE);
    break;
    
    case AT_DEVICE_CTRL_POWER_OFF:
        result = w15_power_enable(device, RT_FALSE);
    break;
    
    case AT_DEVICE_CTRL_GET_SIGNAL:
       result =  w15_wifi_get_signal(device, (int16_t *)arg);
    break;
    
    case AT_DEVICE_CTRL_LOW_POWER:
    case AT_DEVICE_CTRL_SLEEP:
    case AT_DEVICE_CTRL_WAKEUP:
    case AT_DEVICE_CTRL_NET_CONN:
    case AT_DEVICE_CTRL_NET_DISCONN:
    case AT_DEVICE_CTRL_GET_GPS:
    case AT_DEVICE_CTRL_GET_VER:
    break;
    
    case AT_DEVICE_CTRL_RESET:
        result = w15_reset(device);
    break;
    
    case AT_DEVICE_CTRL_SET_WIFI_INFO:
        result = w15_wifi_info_set(device, (struct at_device_ssid_pwd *) arg);
    break;
    
    default:
        LOG_E("input error control cmd(%d).", cmd);
    break;
    }

    return result;
}

static const struct at_device_ops w15_device_ops =
{
    w15_init,
    w15_deinit,
    w15_control,
};

int w15_device_class_register(void)
{
    struct at_device_class *class = RT_NULL;

    class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class));
    if (class == RT_NULL) {
        LOG_E("no memory for w15 class create.");
        return -RT_ENOMEM;
    }

    /* fill W15 device class object */
#ifdef AT_USING_SOCKET
    w15_socket_class_register(class);
#endif
    class->device_ops = &w15_device_ops;

    return at_device_class_register(class, AT_DEVICE_CLASS_W15);
}

w15_device_info w15_device_get_info(void)
{
	return w15_info;
}

void w15_ping_resp_clear(void)
{
    rt_memset(ping_resp_str, 0, sizeof(ping_resp_str));
}

char *w15_ping_resp_get(void)
{
    return ping_resp_str;
}

int w15_server_enable(struct at_device *device, rt_bool_t is_enabled)
{
    int retry_times = 0;
    at_response_t resp = RT_NULL;
    int status, result = RT_ERROR;
    rt_bool_t not_change = RT_FALSE;
    static rt_bool_t default_config_disabled = RT_FALSE;
    
    resp = at_create_resp(256, 0, RT_TICK_PER_SECOND);
    if (resp == RT_NULL ) {
        LOG_E("no memory for resp create.");
        goto __exit;
    }
    
    if (default_config_disabled == RT_FALSE) {
        /* disable default server config */
        while (1) {
            if (at_obj_exec_cmd(device->client, resp, "AT+UDSC=0,0") < 0) {
                LOG_E("disable device(%s) server default config error, try again...", device->name);
                rt_thread_mdelay(1000);
                continue;
            }
            default_config_disabled = RT_TRUE;
            break;
        }
    }
    
    while (1) {
        if (at_obj_exec_cmd(device->client, resp, "AT+UDSC=0") < 0) {
            LOG_E("get device(%s) server status failed.", device->name);
            goto __exit;
        }
        
        if (at_resp_parse_line_args_by_kw(resp, "+UDSC:", "+UDSC:0,%d", &status) <= 0) {
            LOG_E("parse device(%s) server status failed.", device->name);
            goto __exit;
        }
    
        if (is_enabled) {
            if (status != 0) {
                result = RT_EOK;
                not_change = RT_TRUE;
                goto __exit;
            }
            
            /* set server id and port (type is TCP, port is 80, disable immediate flush after a write)*/
            //if (at_obj_exec_cmd(device->client, resp, "AT+UDSC=0,at-tcp://0.0.0.0:80/?flush_tx=1") < 0) {
            if (at_obj_exec_cmd(device->client, resp, "AT+UDSC=0,at-tcp://0.0.0.0:80") < 0) {
                LOG_E("enable device(%s) server error.", device->name);
                goto __exit;
            }
            result = RT_EOK;
        } else {
            if (status == 0) {
                result = RT_EOK;
                not_change = RT_TRUE;
                goto __exit;
            }
            
            /* disable server */
            if (at_obj_exec_cmd(device->client, resp, "AT+UDSC=0,0") < 0) {
                LOG_E("disable device(%s) server error.", device->name);
                goto __exit;
            }
            result = RT_EOK;
        }
        
__exit:
        
        if (result != RT_EOK && retry_times < 3) {
            LOG_W("%s device(%s) server retry...", is_enabled ? "enable" : "disable", device->name);
            retry_times ++;
            continue;
        }
        
        if (result == RT_EOK) {
            if (not_change == RT_TRUE) {
                LOG_W("device(%s) server has already %s.", device->name, is_enabled ? "enabled" : "disabled");
            } else {
                LOG_I("device(%s) server %s.", device->name, is_enabled ? "enabled" : "disabled");
            }
        } else {
            LOG_I("if there is one connection that still holds, enable server will not success, please check it.");
        }
        
        break;
    }
    
    if (resp) {
        at_delete_resp(resp);
    }
    
    return result;
}

/* these functions are for test w15 mudoule */
#if 1
static int wifi(int argc, char **argv)
{
    int result = RT_ERROR;
    char *argv2, *argv3, *argv4;
    struct at_device *device = RT_NULL;
    
    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, W15_DEIVCE_NAME);
    if (device == RT_NULL) {
        rt_kprintf("device(w15) not found.");
        result = RT_EOK;
    }
    
    if (!rt_strcmp(argv[1], "1")) {
        
        w15_wifi_enable(device, RT_TRUE);
        result = RT_EOK;
        
    } else if (!rt_strcmp(argv[1], "0")) {
        
        w15_wifi_enable(device, RT_FALSE);
        result = RT_EOK;
        
    } else if (!rt_strcmp(argv[1], "dhcp")) {
        
        if (!rt_strcmp(argv[2], "1")) {
            w15_netdev_set_dhcp(device->netdev, RT_TRUE);
            result = RT_EOK;
        } else if (!rt_strcmp(argv[2], "0")) {
            w15_netdev_set_dhcp(device->netdev, RT_FALSE);
            result = RT_EOK;
        } 
        
    } else if (!rt_strcmp(argv[1], "dns")) {
        
        if (argc == 3) {
            w15_netdev_set_dns_server_by_str(device->netdev, argv[2], RT_NULL);
            result = RT_EOK;
        } else if (argc == 4) {
            w15_netdev_set_dns_server_by_str(device->netdev, argv[2], argv[3]);
            result = RT_EOK;
        }
        
    } else if (!rt_strcmp(argv[1], "addr") && argc == 5) {

        if (rt_strcmp(argv[2], "0")) {
            argv2 = argv[2];
        }
        if (rt_strcmp(argv[3], "0")) {
            argv3 = argv[3];
        }
        if (rt_strcmp(argv[4], "0")) {
            argv4 = argv[4];
        }
        
        w15_netdev_set_addr_info_by_str(device->netdev, argv2, argv3, argv4);
        result = RT_EOK;
    }
    
__exit:
    
    if (result != RT_EOK) {
        rt_kprintf("Usage:\n");
        rt_kprintf("wifi <1/0>                  - wifi enable or disable\n");
        rt_kprintf("wifi dhcp <1/0>             - wifi dhcp control\n");
        rt_kprintf("wifi dns <dns_server0> [dns_server1]\n");
        rt_kprintf("                            - set wifi dns server\n");
        rt_kprintf("wifi addr <ipv4> <netmask> <gateway>\n");
        rt_kprintf("                            - set wifi ipv4, netmask and gateway\n");
    }
    
    return result;
}
MSH_CMD_EXPORT(wifi, w15 device wifi control);

static int hotspot(int argc, char **argv)
{
    int result = RT_EOK;
    struct at_device *device = RT_NULL;
    
    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, W15_DEIVCE_NAME);
    if (device == RT_NULL) {
        rt_kprintf("device(w15) not found.");
        return result;
    }
    
    if (!rt_strcmp(argv[1], "1")) {
        w15_hotspot_enable(device, RT_TRUE);
        return result;
    } else if (!rt_strcmp(argv[1], "0")) {
        w15_hotspot_enable(device, RT_FALSE);
        return result;
    }
    
__exit:
    
    rt_kprintf("Usage:\n");
    rt_kprintf("hotspot <1/0>                  - hotspot enable or disable\n");
    
    return result;
}
MSH_CMD_EXPORT(hotspot, w15 device hotspot control);

static int server(int argc, char **argv)
{
    int result = RT_EOK;
    struct at_device *device = RT_NULL;
    
    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, W15_DEIVCE_NAME);
    if (device == RT_NULL) {
        rt_kprintf("device(w15) not found.");
        return result;
    }
    
    if (!rt_strcmp(argv[1], "1")) {
        w15_server_enable(device, RT_TRUE);
        return result;
    } else if (!rt_strcmp(argv[1], "0")) {
        w15_server_enable(device, RT_FALSE);
        return result;
    }
    
__exit:
    
    rt_kprintf("Usage:\n");
    rt_kprintf("server <1/0>                  - server enable or disable\n");
    
    return result;
}
MSH_CMD_EXPORT(server, w15 device server control);

static int peers(int argc, char **argv)
{
    int result = RT_EOK;
    struct at_device *device = RT_NULL;
    
    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, W15_DEIVCE_NAME);
    if (device == RT_NULL) {
        rt_kprintf("device(w15) not found.");
        return result;
    }
    
    w15_netdev_netstat(device->netdev);

    return result;
}
MSH_CMD_EXPORT(peers, get w15 device connection peers);
#endif

#endif /* AT_DEVICE_USING_W15 */
